Conversation
|
|
||
| ## What changes for the evaluator | ||
|
|
||
| In multi-user mode, the evaluator already uses the daemon protocol for store operations. What changes is the **execution environment**: the evaluator process is now confined to a sandbox with no network access and restricted filesystem access. `builtins.fetchurl` and `builtins.path` continue to go through the daemon — but now this is the *only* way to perform I/O, enforced by the sandbox rather than by language semantics. |
There was a problem hiding this comment.
“
builtins.fetchurlandbuiltins.path[…] now this is the only way[s] to perform I/O”
For builtins.path, the evaluator process performs the actual file read or at least needs to open the file and pass the fd to the daemon. Otherwise, as I've touched on above, we would read everything as root.
Also note that forcing something akin to builtins.path (i.e. copying to store) for readFile and import would likely create an unreasonable overhead.
|
|
||
| ## What happens to IFD and Recursive Nix | ||
|
|
||
| They stop being special cases. IFD is currently "evaluation pauses, triggers a build, resumes with the result" — a special code path. Under this proposal, the evaluator simply sends a build request through the daemon socket and reads back the output path. This is an ordinary protocol interaction, the same thing builders already do with Recursive Nix. The distinction between "evaluation-time store access" and "build-time store access" disappears because both go through the same socket in the same kind of sandbox. |
There was a problem hiding this comment.
I do not believe you are describing this correctly. Evaluation pauses in the IFD case not because of some “special case", but because the evaluator has to wait for the file it wants to read to become available (and can't do anything else because it isn't multithreaded), so nothing would change, but we would also not gain anything.
| |-- evaluator runs inside sandbox | ||
| |-- daemon socket mounted (same mechanism as builds) | ||
| |-- no network access | ||
| |-- filesystem restricted to source inputs |
There was a problem hiding this comment.
This is really the main problem of this proposal and will need to be elaborated on.
It is completely impossible to determine the “source inputs” of an evaluation without performing said evaluation.
For that purpose, it is actually better that the evaluator has full file system access (at least to read) since performing those reads over the daemon protocol would mean that we would need to add an interface for performing arbitrary filesystem reads to the daemon which runs as reads. This would create the potential for new exploits.
There was a problem hiding this comment.
Since it's mentioned above that the relevant information comes from the CLI, I'm guessing "source inputs" just means "initial source files to be evaluated" e.g. a flake. And that evaluation would determine if anything needs to be fetched. This is further supported by what is said in the examples. So, if that's what it means, it isn't impossible but we'll need a round trip once we find anything that needs to be fetched before further evaluation, yes?
There was a problem hiding this comment.
Strictly speaking, the initial input you learn from the CLI is a single file. While evaluating it, you will learn about more files you need to read in order to properly evaluate it (in contrast to a less dynamic language with a module system or similar where you would be able to find this information statically).
With flakes and/or pure eval, you would be able to determine a maximum set of available files for evaluation and make them available to the evaluator in the sandbox. The slight problem being, of course, that neither is a stable feature of Nix in the first place. Also, in order for the proposal to be properly implemented based on those feature, flakes and pure eval would not only need to be stabilized, but also the only evaluation mode available which is not a desirable goal in the first place.
| - **Protocol commitment.** A stable daemon protocol is a long-term maintenance burden. | ||
| - **Community.** This deliberately levels the Nix language's special status, which may face resistance. | ||
|
|
||
| # Alternatives |
There was a problem hiding this comment.
Can't you just run the evaluator inside a user space sandbox of some description to get the same guarantees?
| - **Protocol commitment.** A stable daemon protocol is a long-term maintenance burden. | ||
| - **Community.** This deliberately levels the Nix language's special status, which may face resistance. | ||
|
|
||
| # Alternatives |
There was a problem hiding this comment.
Can't you just run the evaluator inside a user space sandbox of some description to get the same guarantees?
|
|
||
| ## What changes for alternative frontends | ||
|
|
||
| Nothing special. Any program that can speak the daemon protocol through a Unix socket can produce derivations. A Python script, a Rust binary, a Guile program — all run inside the same sandbox with the same daemon socket. The Nix language becomes one frontend among many, distinguished only by the size of its ecosystem (nixpkgs), not by architectural privilege. |
There was a problem hiding this comment.
I also do not fully understand this angle. This proposal is not strictly necessary for alternative frontends to be feasible.
|
|
||
| ## What changes for alternative frontends | ||
|
|
||
| Nothing special. Any program that can speak the daemon protocol through a Unix socket can produce derivations. A Python script, a Rust binary, a Guile program — all run inside the same sandbox with the same daemon socket. The Nix language becomes one frontend among many, distinguished only by the size of its ecosystem (nixpkgs), not by architectural privilege. |
There was a problem hiding this comment.
I also do not fully understand this angle. This proposal is not strictly necessary for alternative frontends to be feasible.
|
|
||
| This asymmetry gives the Nix language a privileged position: | ||
|
|
||
| - `nix build`, `nix develop`, `nix flake` — all CLI entry points invoke the Nix evaluator. There is no sanctioned path to the store that does not go through the Nix language. |
There was a problem hiding this comment.
nix-store --addon a valid derivation works.nix derivation addtakes JSON and creates derivations.- Guix was in fact sharing the daemon and the store just fine for some time, until they decided to rewrite it.
| - **Performance.** Sandboxing the evaluator adds overhead for sandbox creation and teardown. In single-user mode, where the evaluator currently accesses the store directly (bypassing the daemon), switching to daemon-mediated access would add per-call latency. In multi-user mode the daemon path is already used, so the additional cost is only the sandbox itself. | ||
| - **Bootstrapping.** The evaluator binary may reside in the store. The daemon must make it available inside the sandbox before evaluation starts, similar to how it provisions builders. | ||
| - **Protocol commitment.** A stable daemon protocol is a long-term maintenance burden. | ||
| - **Community.** This deliberately levels the Nix language's special status, which may face resistance. |
There was a problem hiding this comment.
Does this lose the current fetch-without-checksum-and-cache functionality?
|
So, of the three goals:
|
|
Thank you all for the thoughtful feedback — it was genuinely helpful. After reflecting on the discussion, I've realized that the use case I had in mind doesn't actually require what this RFC proposes. My motivation was enabling a non-Nix language to leverage nixpkgs and other Nix-defined packages via Recursive Nix builds. I assumed the evaluator's I/O builtins would need to be rerouted through the daemon to work inside a build sandbox, but that turns out to be unnecessary:
On the bright side, this means Recursive Nix is already good enough to enable non-Nix language interop with the Nix ecosystem without any changes to the evaluator. That's a nice place to be. Closing this RFC. Thanks again for taking the time to review it. |
This RFC proposes that the Nix evaluator should not be treated as a special, privileged component. Instead, it should run inside a sandbox and interact with the store exclusively through the daemon socket — the same way builders already do via Recursive Nix. This generalizes the evaluator into just another program that talks to the Nix daemon from inside a sandbox.
This generalization enables:
Rendered: 00199-sandbox-nix-evaluator.md